#ifndef __ZHONG_ETCD__
#define __ZHONG_ETCD__

#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Watcher.hpp>
#include <etcd/Value.hpp>
#include <thread>
#include <functional>
#include <iostream>
#include "logger.hpp"

// namespace zhong
// {
//     // 对etcd的二次封装
//     namespace etcder
//     {

//     }
// }

namespace bite_im
{
    // 注册服务类
    class Registry
    {
    public:
        using ptr = std::shared_ptr<Registry>;
        Registry(const std::string &local_host)
            : _client(std::make_shared<etcd::Client>(local_host)),
              _keep_alive(_client->leasekeepalive(3).get()),
              _lease_id(_keep_alive->Lease()) {}
        ~Registry()
        {
            _keep_alive->Cancel();
        }
        bool registry(const std::string &service_name, const std::string &dest_host)
        {
            // 注意put传入_lease_id才能够实时下线
            auto response = _client->put(service_name, dest_host, _lease_id).get();
            if (response.is_ok() == false)
            {
                LOG_ERROR("新增键值对[{}]-[{}]失败, 原因[{}]", service_name, dest_host, response.error_message());
                return false;
            }
            LOG_DEBUG("新增键值对[{}]-[{}]", service_name, dest_host);
            return true;
        }

    private:
        std::shared_ptr<etcd::Client> _client;
        std::shared_ptr<etcd::KeepAlive> _keep_alive;
        uint64_t _lease_id;
    };
    // 发现服务类
    class Discovery
    {
    public:
        using ptr = std::shared_ptr<Discovery>;
        using NotifyCallback = std::function<void(std::string, std::string)>;
        Discovery(const std::string &service_dir,
                  const std::string &dest_host,
                  const NotifyCallback &put_cb,
                  const NotifyCallback &del_cb)
            : _client(std::make_shared<etcd::Client>(dest_host)),
              _put_callback(put_cb),
              _del_callback(del_cb)
        {
            // 进行服务发现
            auto response = _client->ls(service_dir).get(); // response.key(i) 对应着address_host, 而response.value(i)为服务名
            if (response.is_ok() == false)
            {
                LOG_ERROR("获取服务数据失败, 原因[{}]", response.error_message());
                return;
            }
            int size = response.keys().size();
            for (int i = 0; i < size; i++)
            {
                // std::cout << __FILE__ << ":" << __LINE__ << "提供服务内容如下：" << std::endl;
                // std::cout << "主机[" << response.value(i).as_string() << "]提供服务[" << response.key(i) << "]" << std::endl;
                if (_put_callback != nullptr)
                {
                    _put_callback(response.key(i), response.value(i).as_string());
                }
                _watcher = std::make_shared<etcd::Watcher>(*_client.get(), service_dir,
                                                           std::bind(&Discovery::callback, this, std::placeholders::_1), true);
            }
        }

    private:
        void callback(const etcd::Response &response)
        {
            if (response.is_ok() == false)
            {
                LOG_ERROR("收到错误事件, 原因[{}]", response.error_message());
                return;
            }

            // .events() 返回存储事件的vector
            for (auto event : response.events())
            {
                // 获取事件类型
                auto type = event.event_type();

                if (type == etcd::Event::EventType::PUT)
                {
                    if (_put_callback != nullptr)
                    {
                        _put_callback(event.kv().key(), event.kv().as_string());
                    }
                    LOG_INFO("服务[{}]-[{}]新增", event.kv().key(), event.kv().as_string());
                }
                else if (type == etcd::Event::EventType::DELETE_)
                {
                    if (_del_callback != nullptr)
                    {
                        _del_callback(event.prev_kv().key(), event.prev_kv().as_string());
                    }
                    LOG_INFO("服务[{}]-[{}]下线", event.prev_kv().key(), event.prev_kv().as_string());
                }
            }
        }

    private:
        NotifyCallback _put_callback;
        NotifyCallback _del_callback;
        std::shared_ptr<etcd::Client> _client;
        std::shared_ptr<etcd::Watcher> _watcher;
    };
}

#endif
